package com.pinfly.common.crypto;

import java.io.ByteArrayOutputStream;

import com.pinfly.common.util.HexConvert;

/**
 * A stream used to write X509 and Microsoft RSA keys
 */
public class KeyOutputStream extends ByteArrayOutputStream
{
    private boolean m_useLittleEndian = false;

    /**
     * Creates a new instance
     * 
     * @param useLittleEndian If true, it indicates that the data being written
     *            out by this stream should use Little-endian format, otherwise
     *            use Big-endian. Note: MS RSA keys are in Little-endian format.
     */
    public KeyOutputStream (boolean useLittleEndian)
    {
        super ();
        m_useLittleEndian = useLittleEndian;
    }

    /**
     * Writes the byte to the stream.
     * 
     * @param b The byte to write
     */
    public void write (byte b)
    {
        // Interesting note, originally I left out this method, so that
        // the inherited method would be called. What I discovered was that
        // write(short s) of this class was being called instead!

        super.write (b);
    }

    /**
     * Writes the short to the stream. If useLittleEndian was set to true in the
     * constructor for this class, then the bytes written to the stream are
     * reversed before being written.
     * 
     * @param s The short to write
     */
    public void write (short s)
    {
        // Convert the short into 2 bytes
        StringBuffer buf = new StringBuffer (Integer.toHexString (s));

        // Integer.toHexString doesn't include leading zeros
        while (buf.length () != 4)
        {
            buf.insert (0, "0");
        }

        // Convert into a byte array
        byte[] results = HexConvert.convertToBytes (buf.toString ());

        // Write it out
        write (results);
    }

    /**
     * Writes an int to the stream. If useLittleEndian was set to true in the
     * constructor for this class, then the bytes written to the stream are
     * reversed before being written.
     * 
     * @param b The int to write
     */
    public void write (int b)
    {
        // Convert the int into 4 bytes
        StringBuffer buf = new StringBuffer (Integer.toHexString (b));

        // Integer.toHexString doesn't include leading zeros
        while (buf.length () != 8)
        {
            buf.insert (0, "0");
        }

        // Convert into a byte array
        byte[] results = HexConvert.convertToBytes (buf.toString ());

        // Write it out
        write (results);
    }

    /**
     * Writes the array of bytes to the stream. If useLittleEndian was set to
     * true in the constructor for this class, then the bytes written to the
     * stream are reversed before being written.
     * 
     * @param input The bytes to write
     */
    public void write (byte[] input)
    {
        write (input, 0, input.length);
    }

    /**
     * Writes len bytes from the specified byte array starting at offset off to
     * this byte array output stream. If useLittleEndian was set to true in the
     * constructor for this class, then the bytes written to the stream are
     * reversed before being written.
     * 
     * @param b the data.
     * @param off the start offset in the data.
     * @param len the number of bytes to write.
     */
    public void write (byte[] b, int off, int len)
    {
        if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0))
        {
            throw new IndexOutOfBoundsException ();
        }
        else if (len == 0)
        {
            return;
        }

        byte[] tmp = new byte[len];
        System.arraycopy (b, off, tmp, 0, len);

        if (m_useLittleEndian)
        {
            // Reverse the bytes
            KeyUtil.reverseBytes (tmp);
        }

        super.write (tmp, 0, tmp.length);
    }

}
